package ws.very.util.lang
import java.util.{ concurrent => juc }
import scala.concurrent.{ ExecutionContext, Await, ExecutionException }
import scala.concurrent.duration.Duration
import scala.concurrent.Future
import scala.concurrent.Promise
object RichFuture {
 
  def promiseFuture[T](promiseComplete:Promise[T]=>Unit)={
      val p = Promise[T]()
      promiseComplete(p)
      p.future
  }
  object FutureEither {

    class LeftProjection[+A, +B](underlying: Future[Either[A, B]])(implicit executor: ExecutionContext) {

      def flatMap[BB >: B, X](f: A => Future[Either[X, BB]]): Future[Either[X, BB]] =
        underlying.flatMap {
          _.fold(a => f(a),
            b => Future.successful(Right(b)))
        }

      def map[X](f: A => X): Future[Either[X, B]] =
        underlying.map {
          _.left.map(f)
        }

      def foreach[U](f: A => U) {
        underlying.foreach { _.left.foreach(f) }
      }
    }
    class RightProjection[+A, +B](underlying: Future[Either[A, B]])(implicit executor: ExecutionContext) {
      def flatMap[AA >: A, Y](f: B => Future[Either[AA, Y]]): Future[Either[AA, Y]] =
        underlying.flatMap { eth =>
          eth.fold(a => Future.successful(Left(a)),
            b => f(b))
        }

      def map[Y](f: B => Y): Future[Either[A, Y]] =
        underlying.map {
          _.right.map(f)
        }

      def foreach(f: B => Unit) {
        underlying.foreach { _.right.foreach(f) }
      }
      def values[A1 >: A, C](implicit ev: RightProjection[A, B] <:< RightProjection[A1, Iterable[C]]) =
        new FutureRightIterable.Values(underlying, this)
    }
  }

  object FutureIterable {

    class Flatten[A](val underlying: Future[Iterable[A]])(implicit executor: ExecutionContext) {

      def flatMap[Iter[B] <: Iterable[B], B](f: A => Future[Iter[B]]) =
        underlying.flatMap { iter =>
          Future.sequence(iter.map(f)).map { _.flatten }
        }
      def map[Iter[B] <: Iterable[B], B](f: A => Iter[B]): Future[Iterable[B]] =
        underlying.map { _.map(f) }.map { _.flatten }
      def foreach(f: A => Unit) {
        underlying.foreach { _.foreach(f) }
      }
      def withFilter(p: A => Boolean) =
        new Flatten(underlying.map { _.filter(p) })
      def filter(p: A => Boolean) = withFilter(p)
    }

    class Values[A](underlying: Future[Iterable[A]])(implicit executor: ExecutionContext) {
      def flatMap[B](f: A => Future[B]) =
        underlying.flatMap { iter =>
          Future.sequence(iter.map(f))
        }
      def map[B](f: A => B): Future[Iterable[B]] =
        underlying.map { _.map(f) }
      def foreach(f: A => Unit) {
        underlying.foreach { _.foreach(f) }
      }
      def withFilter(p: A => Boolean) =
        new Values(underlying.map { _.filter(p) })
      def filter(p: A => Boolean) = withFilter(p)
      def flatten = new Flatten(underlying)
    }
  }
  object FutureRightIterable {
    import FutureEither.RightProjection
    type RightIter[E, A] = RightProjection[E, Iterable[A]]

    private def flatRight[L, R](eithers: Iterable[Either[L, R]]) = {
      val start: Either[L, Seq[R]] = Right(Seq.empty)
      (start /: eithers) { (a, e) =>
        for {
          seq <- a.right
          cur <- e.right
        } yield (seq :+ cur)
      }
    }
    class Flatten[E, A](parent: Future[_], underlying: RightIter[E, A])(implicit executor: ExecutionContext) {
      def flatMap[Iter[B] <: Iterable[B], B](f: A => Future[Either[E, Iter[B]]]) =
        underlying.flatMap { iter =>
          Future.sequence(iter.map(f)).map { eths =>
            flatRight(eths).right.map { _.flatten }
          }
        }
      def map[Iter[B] <: Iterable[B], B](f: A => Iter[B]) =
        underlying.flatMap { iter =>
          Future.successful(Right(iter.map(f).flatten))
        }
      def foreach(f: A => Unit) {
        underlying.foreach { _.foreach(f) }
      }
      def withFilter(p: A => Boolean) =
        new Values(parent, underlying.map { _.filter(p) }.right)
      def filter(p: A => Boolean) = withFilter(p)
    }
    class Values[E, A](parent: Future[_], underlying: RightIter[E, A])(implicit executor: ExecutionContext) {

      def flatMap[B](f: A => Future[Either[E, B]]) =
        underlying.flatMap { iter =>
          Future.sequence(iter.map(f)).map(flatRight)
        }
      def map[B](f: A => B) =
        underlying.flatMap { iter =>
          Future.successful(Right(iter.map(f)))
        }
      def foreach(f: A => Unit) {
        underlying.foreach { _.foreach(f) }
      }
      def flatten = new Flatten(parent, underlying)
      def withFilter(p: A => Boolean) =
        new Values(parent, underlying.map { _.filter(p) }.right)
      def filter(p: A => Boolean) = withFilter(p)
    }
  }

  implicit class EnrichedFuture[A](underlying: Future[A]) {

    /**
     * Project promised value into an either containing the value or any
     *  exception thrown retrieving it. Unwraps `cause` of any top-level
     *  ExecutionException
     */
    def either: Future[Either[Throwable, A]] = {
      implicit val ctx = currentThreadContext

      underlying.map { res => Right(res) }.recover {
        case exc: ExecutionException => Left(exc.getCause)
        case throwable => Left(throwable)
      }
    }
    /** Create a left projection of a contained either */
    def left[B, C](implicit ev: Future[A] <:< Future[Either[B, C]],
      executor: ExecutionContext) =
      new FutureEither.LeftProjection(underlying)

    /** Create a right projection of a contained either */
    def right[B, C](implicit ev: Future[A] <:< Future[Either[B, C]],
      executor: ExecutionContext) =
      new FutureEither.RightProjection(underlying)

    /** Project any resulting exception or result into a unified type X */
    def fold[X](fa: Throwable => X, fb: A => X)(implicit executor: ExecutionContext): Future[X] =
      for (eth <- either) yield eth.fold(fa, fb)

    def flatten[B](implicit pv: Future[A] <:< Future[Future[B]],
      executor: ExecutionContext): Future[B] =
      (underlying: Future[Future[B]]).flatMap(identity)

    /** Facilitates projection over promised iterables */
    def values[B](implicit ev: Future[A] <:< Future[Iterable[B]],
      executor: ExecutionContext) =
      new FutureIterable.Values(underlying)

    /**
     * Project promised value into an Option containing the value if retrived
     *  with no exception
     */
    def option: Future[Option[A]] = {
      implicit val ctx = currentThreadContext
      either.map { _.right.toOption }
    }

    def apply() = Await.result(underlying, Duration.Inf)

    /** Some value if promise is complete, otherwise None */
    def completeOption =
      for (tried <- underlying.value) yield tried.get

    def print =
      "Future(%s)".format(either.completeOption.map {
        case Left(exc) => "!%s!".format(exc.getMessage)
        case Right(value) => value.toString
      }.getOrElse("-incomplete-"))
  }

  object currentThreadContext extends ExecutionContext {
    /** Execute on the current thread, for certain cpu-bound operations */

    def execute(runnable: Runnable) =
      runnable.run()

    def reportFailure(t: Throwable) =
      ExecutionContext.defaultReporter(t)

  }

  implicit class ErrorMessageFuture[A](val future: Future[A]) extends AnyVal {
    def errorMsg(error: String)(implicit executionContext: ExecutionContext): Future[A] = future.recoverWith {
      case t: Throwable => Future.failed(new Exception(error, t))
    }
  }
}